//
//  Copyright 2011  comphilip
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
using System;
using System.IO;
using System.Globalization;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Dict4Engkoo;
using Dict4Engkoo.Processor;
using Trace = System.Diagnostics.Trace;

namespace DictShell {
	internal class DictServer {
		private int _networkPort;
		private TcpListener _listener;
		private bool _debug;

		public DictServer(int Port, bool Debug) {
			this._networkPort = Port;
			_listener = new TcpListener(IPAddress.Parse("127.0.0.1"), this._networkPort);
			_debug = Debug;
		}

		public void Start() {
			_listener.Start();
			_listener.BeginAcceptSocket(EndAcceptSocket, null);
			Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "DICT Server started. Port : {0}", Port));
		}

		public void Stop() {
			_listener.Stop();
		}

		public int Port {
			get {return _networkPort;}
		}

		public bool Debug {
			get {return _debug;}
		}

		private void EndAcceptSocket(IAsyncResult AR) {
			// listen next client
			_listener.BeginAcceptSocket(EndAcceptSocket, null);

			Socket client = _listener.EndAcceptSocket(AR);
			IPEndPoint endPoint = client.RemoteEndPoint as IPEndPoint;
			IPAddress ipAddress = endPoint != null ? endPoint.Address : IPAddress.None;
			DictAsyncContext context = new DictAsyncContext {
				ClientIPAddress = ipAddress,
				Client = client,
				Processor = ProcessorFactory.CreateProcessor()
			};
			using(DictOutputWriter writer = new DictOutputWriter(context.OutputBuffer, Debug)) {
				context.Processor.StartProcessor(new DictContext(ipAddress, string.Empty, writer));
			}
			SendMessage(context, BeginReceiveCommand);
		}

		private void SendMessage(DictAsyncContext Context, AsyncCallback Callback) {
			byte[] output = Encoding.UTF8.GetBytes(Context.OutputBuffer.ToString());
			Context.Client.BeginSend(output, 0, output.Length, SocketFlags.None, Callback, Context);
		}

		private void BeginReceiveCommand(IAsyncResult AR) {
			DictAsyncContext context = AR.AsyncState as DictAsyncContext;
			if (context.Client.Connected)
				context.Client.BeginReceive(context.InputBuffer, 0, context.InputBuffer.Length, SocketFlags.None, EndReceiveCommand, context);
		}

		private void EndReceiveCommand(IAsyncResult AR) {
			DictAsyncContext context = AR.AsyncState as DictAsyncContext;
			int count = context.Client.EndReceive(AR);
			if (count > 0) {
				string command = Encoding.UTF8.GetString(context.InputBuffer, 0, count).Trim('\r', '\n');
				if (_debug)
					Trace.WriteLine(command);
				context.OutputBuffer.Length = 0;
				DictOutputWriter writer = new DictOutputWriter(context.OutputBuffer, Debug);
				context.DictProcessContext = new DictContext(context.ClientIPAddress, command, writer);
				context.Processor.BeginProcessCommand(context.DictProcessContext, EndProcessCommand, context);
			} else
				BeginReceiveCommand(AR);
		}

		private void EndProcessCommand(IAsyncResult AR) {
			DictAsyncContext context = AR.AsyncState as DictAsyncContext;
			try {
				using(TextWriter writer = context.DictProcessContext.OutputWriter) {
					context.Processor.EndProcessCommand(AR);
				}
				SendMessage(context, BeginReceiveCommand);
			} catch (EndDictProcessorException) {
				SendMessage(context, ReleaseContext);
			} catch (DictException exp) {
				Trace.WriteLine(exp.Message);
				Trace.WriteLine(exp.StackTrace);
			}
		}

		private void ReleaseContext(IAsyncResult AR) {
			DictAsyncContext context = AR.AsyncState as DictAsyncContext;
			context.Client.Close();
		}
	}
}

